package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.TopHitsAggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.NestedQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class SearchServiceImpl implements SearchService {

    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    @SneakyThrows
    @Override
    public List<Map<String, Object>> channel(Long c1Id) {
        // 1.根据一级分类id 查询该一级分类下7个置顶的3集分类集合
        Result<List<BaseCategory3>> category3ListByC1Id = albumInfoFeignClient.getBaseCategory3ListByC1Id(c1Id);
        List<BaseCategory3> baseCategory3ListData = category3ListByC1Id.getData();
        if (CollectionUtils.isEmpty(baseCategory3ListData)) {
            throw new GuiguException(201, "远程调用分类信息失败");
        }

        // 2.过滤过去到三级分类的id集合 而且转成FieldValue类型（Es在查询的时候要用到这个类型）
        List<FieldValue> c3IdsFieldValue = baseCategory3ListData.stream().map(baseCategory3 -> {
            return FieldValue.of(baseCategory3.getId());
        }).collect(Collectors.toList());

        // 3.在将三级分类集合转成一个Map
        Map<Long, BaseCategory3> c3IdAndBaseCategory3 = baseCategory3ListData.stream().collect(Collectors.toMap(BaseCategory3::getId, v -> v));

        // 4.构建dsl语句
        SearchRequest searchRequest = buildChannelDsl(c3IdsFieldValue);

        // 5.查询es
        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 6.解析es的返回数据
        List<Map<String, Object>> result = parseChannelData(response, c3IdAndBaseCategory3);

        return result;
    }

    @SneakyThrows
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {

        // 1.构建带条件的查询dsl语句
        SearchRequest searchRequest = buildSearchDsl(albumIndexQuery);

        // 2.开始查询es,得到响应对象
        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

        // 3.解析从es中返回的数据，封装给前端
        AlbumSearchResponseVo albumSearchResponseVo = parseSearchData(response);
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        Long total = albumSearchResponseVo.getTotal();
        Long totalPages = total % albumIndexQuery.getPageSize() == 0 ? total / albumIndexQuery.getPageSize() : total / albumIndexQuery.getPageSize() + 1;
        albumSearchResponseVo.setTotalPages(totalPages);  // 总页数
        // 4.返回数据给前端
        return albumSearchResponseVo;

    }

    @SneakyThrows
    @Override
    public Set<String> completeSuggest(String input) {
        // 构建dsl语句
        SearchRequest searchRequest = buildCompletionDsl(input);
        // 开始查询
        SearchResponse<SuggestIndex> response = elasticsearchClient.search(searchRequest, SuggestIndex.class);
        // 解析数据
        Set<String> set = parseCompletionData(response);
        if (set.size() < 10) {
            // 补动作  开头的找不到10个 从包含中找几个  补够10个
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(sb -> sb.index("suggestinfo").query(qb -> qb.match(mqb -> mqb.field("title").query(input))), SuggestIndex.class);
            for (Hit<SuggestIndex> hit : searchResponse.hits().hits()) {
                SuggestIndex suggestIndex = hit.source();
                String title = suggestIndex.getTitle();
                set.add(title);
                if (set.size() >= 10)
                    break;
            }
        }
        // 4.返回
        return set;
    }


    private Set<String> parseCompletionData(SearchResponse<SuggestIndex> response) {
        HashSet<String> result = new HashSet<>();

        Map<String, List<Suggestion<SuggestIndex>>> map = response.suggest();

        for (Map.Entry<String, List<Suggestion<SuggestIndex>>> stringListEntry : map.entrySet()) {   // 遍历三次

            List<Suggestion<SuggestIndex>> value = stringListEntry.getValue();//
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : value) {   // 遍历一次
                List<CompletionSuggestOption<SuggestIndex>> options = suggestIndexSuggestion.completion().options();
                for (CompletionSuggestOption<SuggestIndex> option : options) {
                    SuggestIndex suggestIndex = option.source();
                    String title = suggestIndex.getTitle();// 去显示的
                    result.add(title);

                }
            }
        }
        return result;
    }

    private SearchRequest buildCompletionDsl(String input) {
        // 创建SearchRequestBuilder对象
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();

        // 创建SuggesterBuilder对象
        Suggester.Builder suggesterBuilder = new Suggester.Builder();


        //  拼接提示词的dsl
        suggesterBuilder
                .suggesters("suggestionKeyword", fsb -> fsb
                        .prefix(input)
                        .completion(csb -> csb
                                .field("keyword")
                        )
                )
                .suggesters("suggestionKeywordPinyin", fsb -> fsb
                        .prefix(input)
                        .completion(csb -> csb
                                .field("keywordPinyin")
                        )
                )
                .suggesters("suggestionKeywordSequence", fsb -> fsb
                        .prefix(input)
                        .completion(csb -> csb
                                .field("keywordSequence")
                        )
                );

        // 得到suggester
        Suggester suggester = suggesterBuilder.build();

        SearchRequest searchRequest = searchRequestBuilder.index("suggestinfo").suggest(suggester).build();

        System.out.println("提示词的dsl:" + searchRequest.toString());

        return searchRequest;
    }

    private AlbumSearchResponseVo parseSearchData(SearchResponse<AlbumInfoIndex> response) {
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();

        TotalHits totalHits = response.hits().total();
        long value = totalHits.value();
        albumSearchResponseVo.setTotal(value); // 总专辑数

        List<AlbumInfoIndexVo> albumInfoIndexVoList = response.hits().hits().stream().map(albumInfoIndexHit -> {

            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            AlbumInfoIndex albumInfoIndex = albumInfoIndexHit.source();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            // 处理高亮
            Map<String, List<String>> highlight = albumInfoIndexHit.highlight();
            List<String> albumTitle = highlight.get("albumTitle");
            if (!CollectionUtils.isEmpty(albumTitle)) {
                String highlightAlbumTitle = albumTitle.get(0);
                albumInfoIndexVo.setAlbumTitle(highlightAlbumTitle); // 处理高亮
            }
            return albumInfoIndexVo;
        }).collect(Collectors.toList());

        albumSearchResponseVo.setList(albumInfoIndexVoList);   // 专辑集合
        return albumSearchResponseVo;
    }

    private SearchRequest buildSearchDsl(AlbumIndexQuery albumIndexQuery) {
        //最外层{}
        SearchRequest.Builder searchBuilder = new SearchRequest.Builder();
        //bool查询
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

        //进行查询

        //判断关键字是否携带
        String keyword = albumIndexQuery.getKeyword();

        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.should(qb -> qb
                    .match(mqb -> mqb
                            .field("albumTitle")
                            .query(keyword)
                            .boost(3f)
                    )
            );
            boolQueryBuilder.should(qb -> qb
                    .match(mqb -> mqb
                            .field("albumIntro")
                            .query(keyword)
                            .boost(2f)
                    )
            );
            boolQueryBuilder.should(qb -> qb
                    .match(mqb -> mqb
                            .field("announcerName")
                            .query(keyword)
                            .boost(1f)
                    )
            );
        }
        //判断分类id
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (category1Id != null) {
            boolQueryBuilder.must(qb -> qb
                    .term(tqb -> tqb
                            .field("category1Id").value(category1Id)
                    )
            );
        }
        Long category2Id = albumIndexQuery.getCategory2Id();
        if (category2Id != null) {
            boolQueryBuilder.must(qb -> qb
                    .term(tqb -> tqb
                            .field("category2Id").value(category2Id)
                    )
            );
        }
        Long category3Id = albumIndexQuery.getCategory3Id();
        if (category3Id != null) {
            boolQueryBuilder.must(qb -> qb
                    .term(tqb -> tqb
                            .field("category3Id").value(category3Id)
                    )
            );
        }
        //判断是否携带标签
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (!CollectionUtils.isEmpty(attributeList)) {
            for (String attrAndValueId : attributeList) {
                String[] split = attrAndValueId.split(":");
                String attrId = split[0];
                String valueId = split[1];
                NestedQuery.Builder builder = new NestedQuery.Builder();
                builder.path("attributeValueIndexList")
                        .query(qb -> qb
                                .bool(bqb -> bqb
                                        .must(mqb -> mqb
                                                .term(tqb -> tqb
                                                        .field("attributeValueIndexList.attributeId")
                                                        .value(attrId)
                                                )
                                        )
                                        .must(mqb -> mqb
                                                .term(tqb -> tqb
                                                        .field("attributeValueIndexList.valueId")
                                                        .value(valueId)
                                                )
                                        )
                                )
                        );
                NestedQuery nestedQuery = builder.build();
                boolQueryBuilder.must(qb -> qb.nested(nestedQuery));
            }
        }
        BoolQuery boolQuery = boolQueryBuilder.build();
        Query query = boolQuery._toQuery();
        searchBuilder.index("albuminfo").query(query);

        //编写分页
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        Integer from = (pageNo - 1) * pageSize;
        searchBuilder.from(from).size(pageSize);

        //编写排序
        String order = albumIndexQuery.getOrder();

        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            String orderDimension = split[0]; // 1：热度值 2：购买量 3：发布时间
            String rule = split[1]; // asc:升序 desc:降序
            SortOrder sortOrder = rule.equals("desc") ? SortOrder.Desc : SortOrder.Asc;
            switch (orderDimension) {
                case "1":
                    // 处理热度值
                    searchBuilder.sort(sob -> sob.field(fsb -> fsb.field("hotScore").order(sortOrder)));
                    break;
                case "2":
                    // 处理购买量
                    searchBuilder.sort(sob -> sob.field(fsb -> fsb.field("buyStatNum").order(sortOrder)));
                    break;
                case "3":
                    searchBuilder.sort(sob -> sob.field(fsb -> fsb.field("createTime").order(sortOrder)));
                    // 处理发布时间
            }
        } else {  // 默认热度值降序排序
            searchBuilder.sort(sob -> sob.field(fsb -> fsb.field("hotScore").order(SortOrder.Desc)));
        }

        //高亮
        searchBuilder.highlight(hb -> hb
                .fields("albumTitle", hfb -> hfb
                        .preTags("<font style='color:red'>")
                        .postTags("</font>")
                )
        );

        //返回searchRequest对象
        SearchRequest searchRequest = searchBuilder.build();
        System.out.println("搜索的dsl语句：" + searchRequest.toString());
        return searchRequest;
    }

    private SearchRequest buildChannelDsl(List<FieldValue> c3IdsFieldValue) {


        // 1.构建SearchRequestBuilder对象
        SearchRequest.Builder builder = new SearchRequest.Builder();
        // 2.构建查询
        builder.index("albuminfo")
                .query(b -> b
                        .terms(tqb -> tqb
                                .field("category3Id")
                                .terms(tqfb -> tqfb
                                        .value(c3IdsFieldValue))))
                .aggregations("category3IdAgg", ab -> ab
                        .terms(tab -> tab
                                .field("category3Id")
                                .size(c3IdsFieldValue.size()))
                        .aggregations("topHitHotScoreAgg", sab -> sab
                                .topHits(thab -> thab
                                        .sort(sob -> sob
                                                .field(fsb -> fsb
                                                        .field("hotScore")
                                                        .order(SortOrder.Desc)))
                                        .size(6))));
        SearchRequest searchRequest = builder.build();
        System.out.println("频道页的dsl:" + searchRequest.toString());
        // 3.返回SearchRequest对象
        return searchRequest;
    }

    private List<Map<String, Object>> parseChannelData(SearchResponse<AlbumInfoIndex> response, Map<Long, BaseCategory3> c3IdAndBaseCategory3) {

        ArrayList<Map<String, Object>> result = new ArrayList<>();


        // 1.得到category3IdAgg聚合对象
        Aggregate category3IdAgg = response.aggregations().get("category3IdAgg");

        // 2.得到Aggregate具体的类型
        LongTermsAggregate longTermsAggregate = category3IdAgg.lterms();

        // 3.得到父聚合的桶
        List<LongTermsBucket> buckets = longTermsAggregate.buckets().array();

        for (LongTermsBucket bucket : buckets) {
            // 得到子聚合
            Aggregate topHitHotscoreAgg = bucket.aggregations().get("topHitHotScoreAgg");
            // 把子聚合转为精准的类型
            TopHitsAggregate topHitsAggregate = topHitHotscoreAgg.topHits();
            ArrayList<AlbumInfoIndex> albumInfoIndices = new ArrayList<>();
            for (Hit<JsonData> hit : topHitsAggregate.hits().hits()) {
                JsonData source = hit.source();
                AlbumInfoIndex albumInfoIndex = JSONObject.parseObject(source.toString(), AlbumInfoIndex.class);
                albumInfoIndices.add(albumInfoIndex);
            }


            HashMap<String, Object> map = new HashMap<>();
            long category3Id = bucket.key();
            map.put("baseCategory3", c3IdAndBaseCategory3.get(category3Id));  // 当前三级分类对象
            map.put("list", albumInfoIndices);  // 当前三级分类下热度值高的前6个专辑集合
            result.add(map);
        }
        return result;
    }
}
